#Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
#!/bin/bash
OPS_PROJECT_PATH=$(cd $(dirname $0); pwd)

function fn_log()
{
	log_level=$1
	log_lineno=$2
	log_content=$3
	log_time=`date "+%Y-%m-%d %H:%M:%S"`
	echo "[$log_time] [$log_level] [$log_lineno] $log_content"
	echo "[$log_time] [$log_level] [$log_lineno] $log_content" >> $OPS_PROJECT_PATH/build.log
}

function fn_add_op_ini_to_canndev()
{
	OP_TYPE=$1
	NPU_TYPE=$2
	info_file_name=$3
	canndev_ini=${CANNDEV_DIR}/ops/built-in/tbe/op_info_cfg/ai_core/${NPU_TYPE,,}/${info_file_name}.ini
    cp ${canndev_ini} ${canndev_ini}.bak

	project_ini=${OPS_PROJECT_PATH}/op_info_cfg/ai_core/${NPU_TYPE,,}/${OP_TYPE,,}.ini
	if test `grep -c "\[${OP_TYPE}\]" "${canndev_ini}"` -eq '0'
	then
		cat ${project_ini} >> ${canndev_ini}
	fi
	fn_log "INFO" $0:$LINENO "Write ini info to canndev: ${canndev_ini}..."
}

function fn_add_cfg_ini_to_canndev()
{
	OP_TYPE=$1
	canndev_binary_ini=${CANNDEV_DIR}/ops/built-in/kernel/binary_script/binary_json_cfg.ini
	if test `grep -c "\[${OP_TYPE}\]" "$canndev_binary_ini"` -eq '0'
	then
		echo [$OP_TYPE] >> ${canndev_binary_ini}
	fi
	fn_log "INFO" $0:$LINENO "Write binary ini to canndev: ${canndev_binary_ini}..."
}

function fn_generate_json_info()
{
	OP_TYPE=$1
	NPU_TYPE=$2
	info_file_name=$3
	canndev_binary_path=$4
	canndev_info_json=$5
	if [ -f "$project_cpp" ]; then
    	cp ${canndev_info_json} ${canndev_info_json}.bak
		fn_log "INFO" $0:$LINENO "Backup cann json info: ${canndev_info_json}.bak"
	fi

	cd ${canndev_binary_path} && chmod +x gen_opinfo_json_from_ini.sh
	bash gen_opinfo_json_from_ini.sh ${NPU_TYPE} ./${info_file_name}.json
	if test `grep -c "\"${OP_TYPE}\"" "${canndev_info_json}"` -eq '0'
	then
		fn_log "ERROR" $0:$LINENO "Generate json info failed: ${canndev_binary_path}/gen_opinfo_json_from_ini.sh"
		exit 1
	fi
	fn_log "INFO" $0:$LINENO "Generate json info: ${canndev_info_json}..."
}

function fn_update_cann_json_info()
{
	OP_TYPE=$1
	canndev_info_json=$2
	cann_info_json=$3

	cp ${cann_info_json} ${cann_info_json}.bak
	if [ $? -ne 0 ]
	then
		fn_log "ERROR" $0:$LINENO "Backup cann json info failed: ${cann_info_json}"
		exit 1
	fi
	fn_log "INFO" $0:$LINENO "Backup cann json info: ${cann_info_json}.bak"

	fn_log "INFO" $0:$LINENO "Begin add ops ${OP_TYPE} json info to cann"
	chmod 777 ${cann_info_json}
	cmd="import build_util; build_util.update_ops_info_json(\"${canndev_info_json}\", \"${cann_info_json}\", \"${OP_TYPE}\")"
	echo ${cmd}
	python3 -c "${cmd}"
	if test `grep -c "${OP_TYPE}" "${cann_info_json}"` -eq '0'
	then
		fn_log "ERROR" $0:$LINENO "Add json info to cann failed: ${cann_info_json}"
		exit 1
	fi
	fn_log "INFO" $0:$LINENO "End add json info to cann: ${cann_info_json}"
}

function fn_add_json_info_to_output()
{
	NPU_TYPE=$1
	cann_info_json=$2
	info_file_name=$3
	output_info_path="${OPS_PROJECT_PATH}/output/packages/built-in/op_impl/ai_core/tbe/config/${NPU_TYPE,,}"
	if [ ! -d "${output_info_path}" ]; then
		mkdir -p ${output_info_path}
	fi
	cp ${cann_info_json} ${output_info_path}
	if [ $(ls -lR ${output_info_path} | grep "^-"|wc -l) -ne 1 ]
	then
		fn_log "ERROR" $0:$LINENO "There is something wrong with ${info_file_name}.json: ${output_info_path}"
		exit 1
	fi
}

function fn_add_impl_to_cann()
{
	OP_TYPE=$1
	project_cpp=$2
	project_py=$3
	cann_impl_path="${ASCEND_HOME_PATH}/ops/built-in/op_impl/ai_core/tbe/impl/dynamic"

	chmod -R 777 ${cann_impl_path} && cp ${project_cpp} ${cann_impl_path}
	if [ $? -ne 0 ]
	then
		exit 1
	fi
	fn_log "INFO" $0:$LINENO "Add cpp file to cann: ${cann_impl_path}/${OP_TYPE,,}.cpp"

	cp ${project_py} ${cann_impl_path}
	if [ $? -ne 0 ]
	then
		exit 1
	fi
	fn_log "INFO" $0:$LINENO "Add python file to cann: ${cann_impl_path}/${OP_TYPE,,}.py"
}

function fn_add_impl_to_canndev()
{
	OP_TYPE=$1
	project_cpp=$2
	project_py=$3
	canndev_impl_path="${CANNDEV_DIR}/ops/built-in/tbe/impl/dynamic"

	cp ${project_cpp} ${canndev_impl_path}
	if [ $? -ne 0 ]
	then
		exit 1
	fi
	fn_log "INFO" $0:$LINENO "Add cpp file to canndev: ${canndev_impl_path}/${OP_TYPE,,}.cpp"

	cp ${project_py} ${canndev_impl_path}
	if [ $? -ne 0 ]
	then
		exit 1
	fi
	fn_log "INFO" $0:$LINENO "Add python file to canndev: ${canndev_impl_path}/${OP_TYPE,,}.py"
}

function fn_generate_fuzz_json()
{
	NPU_TYPE=$1
	canndev_binary_path=$2
	fuzz_json_path=${CANNDEV_DIR}/ops/built-in/kernel/binary_config/${NPU_TYPE,,}/${OP_TYPE}
	rm -rf ${fuzz_json_path}
	fn_log "INFO" $0:$LINENO "Remove last time fuzz json path..."

	fn_log "INFO" $0:$LINENO "generate fuzz json start"
	cd ${canndev_binary_path} && chmod +x binary_fuzz_json.sh && bash binary_fuzz_json.sh $OP_TYPE $NPU_TYPE
	if [ $? -ne 0 ]
	then
		fn_log "ERROR" $0:$LINENO "Generate fuzz json failed."
		exit 1
	fi
	fn_log "INFO" $0:$LINENO "Generate fuzz json: ${fuzz_json_path}/${OP_TYPE,,}.json"
}

function fn_build_ops_binary()
{
	NPU_TYPE=$1
	kernel_out=$2
	canndev_binary_path=$3
	rm -rf ${OPS_PROJECT_PATH}/output/packages/built_in
	fn_log "INFO" $0:$LINENO "Removing last time kernel_out path..."

	fn_log "INFO" $0:$LINENO "build ops binary start"
	cp ${ASCEND_HOME_PATH}/python/site-packages/op_gen/template/bscpp/scripts/bscpp_op_build_binary.sh ${canndev_binary_path}
	cp ${OPS_PROJECT_PATH}/op_tiling/${OP_TYPE,,}_tiling_data.h ${ASCEND_HOME_PATH}/ops/built-in/op_impl/ai_core/tbe/impl/dynamic																			
	
	cd ${canndev_binary_path} && \
	chmod +x bscpp_op_build_binary.sh && \
	bash bscpp_op_build_binary.sh ${kernel_out} ${NPU_TYPE} ${OP_TYPE}
	if [ $? -ne 0 ]
	then
		fn_log "ERROR" $0:$LINENO "Build ops binary failed"
		exit 1
	fi
	rm -f ${ASCEND_HOME_PATH}/ops/built-in/op_impl/ai_core/tbe/impl/dynamic/${OP_TYPE,,}_tiling_data.h 
	fn_log "INFO" $0:$LINENO "build ops binary end, generated $OPS_PROJECT_PATH/output/packages"
}

function fn_add_impl_to_canndev_and_cann()
{
	OP_TYPE=$1
	project_cpp=${OPS_PROJECT_PATH}/impl/dynamic/${OP_TYPE,,}.cpp
    if [ ! -f "$project_cpp" ]
	then
        project_cpp=${OPS_PROJECT_PATH}/impl/dynamic/${OP_TYPE,,}.hpp
        if [ ! -f "$project_cpp" ]
        then
            project_cpp=${OPS_PROJECT_PATH}/impl/dynamic/${OP_TYPE,,}.cc
            if [ ! -f "$project_cpp" ]
            then
                fn_log "ERROR" $0:$LINENO "No impl file exists (cpp, hpp, cc)"
		        exit 1
            fi
        fi
	fi
	project_py=${OPS_PROJECT_PATH}/impl/dynamic/${OP_TYPE,,}.py
	fn_add_impl_to_cann ${OP_TYPE} ${project_cpp} ${project_py}
	fn_add_impl_to_canndev ${OP_TYPE} ${project_cpp} ${project_py}
}

function fn_add_info_to_cann()
{
	OP_TYPE=$1
	NPU_TYPE=$2
	info_file_name=$3
	canndev_binary_path=$4
	canndev_info_json=${canndev_binary_path}/${info_file_name}.json
	cann_info_path=${ASCEND_HOME_PATH}/ops/built-in/op_impl/ai_core/tbe/config/${NPU_TYPE,,}
	cann_info_json=${cann_info_path}/${info_file_name}.json
	chmod +w ${canndev_info_json} ${cann_info_path}

	fn_add_op_ini_to_canndev ${OP_TYPE} ${NPU_TYPE} ${info_file_name}
	fn_add_cfg_ini_to_canndev ${OP_TYPE}
	fn_generate_json_info ${OP_TYPE} ${NPU_TYPE} ${info_file_name} ${canndev_binary_path} ${canndev_info_json}
	fn_update_cann_json_info ${OP_TYPE} ${canndev_info_json} ${cann_info_json}
	fn_add_json_info_to_output ${NPU_TYPE} ${cann_info_json} ${info_file_name}
}

function fn_generate_binary()
{
	NPU_TYPE=$1
	canndev_binary_path=$2
	kernel_out=${OPS_PROJECT_PATH}/output/packages
	fn_generate_fuzz_json ${NPU_TYPE} ${canndev_binary_path}
	fn_build_ops_binary ${NPU_TYPE} ${kernel_out} ${canndev_binary_path}
}

function fn_make_proto_and_tiling()
{
	cd $OPS_PROJECT_PATH/op_proto
	rm -rf build
	cmake -B build
	cd build && make -j
	if [ $? -ne 0 ]
	then
		exit 1
	fi

	output_proto_path="$OPS_PROJECT_PATH/output/packages/vendors/customize/op_proto"
	if [ ! -d "${output_proto_path}" ]; then
		mkdir -p ${output_proto_path}
	fi
	cp libcust_op_proto.so $output_proto_path
	fn_log "INFO" $0:$LINENO "generate libcust_op_proto.so"

	cd $OPS_PROJECT_PATH/op_tiling
	rm -rf build
	cmake -B build
	cd build && make -j
	if [ $? -ne 0 ]
	then
		exit 1
	fi

	output_tiling_path="$OPS_PROJECT_PATH/output/packages/vendors/customize/op_impl/ai_core/tbe/op_tiling"
	if [ ! -d "${output_tiling_path}" ]; then
		mkdir -p ${output_tiling_path}
	fi
	cp liboptiling.so $output_tiling_path
	fn_log "INFO" $0:$LINENO "generate liboptiling.so"
}

function fn_packing()
{
	build_out="${OPS_PROJECT_PATH}/output/build_out"
	if [ ! -d "${build_out}" ]; then
		mkdir -p ${build_out}
	fi
	rm -rf $build_out/*

	cd ${OPS_PROJECT_PATH}/output
	tar -zcvf packages.tar.gz packages

	run_file_name=custom_opp_${SYSTEM_INFO}.run
	cd ${OPS_PROJECT_PATH}/output
	cat install.sh packages.tar.gz > ${run_file_name}
	rm -f packages.tar.gz
	chmod +x ${run_file_name}
	mv ${run_file_name} $build_out
}

function fn_restore_cann()
{
	OP_TYPE=$1
	NPU_TYPE=$2
	info_file_name=$3
	cann_impl_path="${ASCEND_HOME_PATH}/ops/built-in/op_impl/ai_core/tbe/impl/dynamic"
	rm -f ${cann_impl_path}/${OP_TYPE,,}.cpp
	rm -f ${cann_impl_path}/${OP_TYPE,,}.hpp
	rm -f ${cann_impl_path}/${OP_TYPE,,}.cc
	rm -f ${cann_impl_path}/${OP_TYPE,,}.py

	cann_info_json=${ASCEND_HOME_PATH}/ops/built-in/op_impl/ai_core/tbe/config/${NPU_TYPE,,}/${info_file_name}.json
	if [ -f "${cann_info_json}.bak" ]
	then
		rm -f ${cann_info_json}
		mv ${cann_info_json}.bak ${cann_info_json}
	fi
}

function fn_restore_canndev()
{
	OP_TYPE=$1
	NPU_TYPE=$2
	info_file_name=$3
	canndev_binary_path=$4
    canndev_ini=${CANNDEV_DIR}/ops/built-in/tbe/op_info_cfg/ai_core/${NPU_TYPE,,}/${info_file_name}.ini
    if [ -f "${canndev_ini}.bak" ]
	then
		rm -f ${canndev_ini}
		mv ${canndev_ini}.bak ${canndev_ini}
	fi
    
    canndev_info_json=${canndev_binary_path}/${info_file_name}.json

    search_file=$(find ${CANNDEV_DIR} -iname "${OP_TYPE}" -or -iname "${OP_TYPE}.*")
    matched_path=($search_file)
    for path in ${matched_path[@]}
    do
        rm -rf $path
        fn_log "INFO" $0:$LINENO "Remove $path"
    done

    canndev_binary_script=${CANNDEV_DIR}/ops/built-in/kernel/binary_script
	chmod 777 -R ${canndev_binary_script}
    rm -f ${canndev_binary_script}/build_binary_all/out_cmd.sh
    rm -f ${canndev_binary_script}/build_binary_all/opc_cmd.sh
	rm -f ${canndev_binary_script}/_${NPU_TYPE,,}.json

    sed -i -e '/^'${OP_TYPE}',/d' ${canndev_binary_script}/opc_info_${NPU_TYPE}.csv
    fn_log "INFO" $0:$LINENO "Remove ${OP_TYPE} from ${canndev_binary_script}/opc_info_${NPU_TYPE}.csv"
    sed -i -e '/^'${OP_TYPE}',/d' ${canndev_binary_script}/../binary_config/binary_config.csv
    fn_log "INFO" $0:$LINENO "Remove ${OP_TYPE} from ${canndev_binary_script}/../binary_config/binary_config.csv"
    sed -i -e '/^'${OP_TYPE}',/d' ${canndev_binary_script}/aic-${NPU_TYPE,,}-opc-info.csv
    fn_log "INFO" $0:$LINENO "Remove ${OP_TYPE} from ${canndev_binary_script}/aic-${NPU_TYPE,,}-opc-info.csv"
    sed -i -e '/\['${OP_TYPE}'\]/d' ${canndev_binary_script}/binary_json_cfg.ini  
    fn_log "INFO" $0:$LINENO "Remove ${OP_TYPE} from ${canndev_binary_script}/binary_json_cfg.ini"

}

function get_name()
{
	cmd="import build_util; build_util.print_kernel_name(\"${ini_file}\")"
	python3 -c "${cmd}"
}

function fn_clear_output()
{
	rm -rf ${OPS_PROJECT_PATH}/output/packages
	rm -rf ${OPS_PROJECT_PATH}/output/build_out
}

function fn_main()
{
	fn_log "INFO" $0:$LINENO "excute file: $0"
	if [ $# != 1 ]; then
		fn_log "ERROR" $0:$LINENO "input error"
		fn_log "ERROR" $0:$LINENO "bash $0 [Ascend910/Ascend310P]"
		exit 1
	fi

	NPU_TYPE=$1
	if [ $NPU_TYPE != Ascend910 ] && [ $NPU_TYPE != Ascend310P ]; then
		fn_log "ERROR" $0:$LINENO "input error"
		fn_log "ERROR" $0:$LINENO "NPU_TYPE only support Ascend910 or Ascend310P"
		exit 1
	fi

	include_file=$(find / | grep securec.h -m 1)
	if [ ! $include_file ]; then
		fn_log "ERROR" $0:$LINENO "There is no securec.h in this dev, which is needed. Try to install Ascend Driver."
		exit 1
	fi
	include_file_path=${include_file%/*}
	export CPLUS_INCLUDE_PATH=$include_file_path:$CPLUS_INCLUDE_PATH

	if [ ! -f ${include_file_path}/securectype.h ]; then
		include_file=$(find / | grep securectype.h -m 1)
		if [ ! $include_file ]; then
			fn_log "ERROR" $0:$LINENO "There is no securectype.h in this dev, which is needed. Try to install Ascend Driver."
			exit 1
		fi
		include_file_path=${include_file%/*}
		export CPLUS_INCLUDE_PATH=$include_file_path:$CPLUS_INCLUDE_PATH
	fi

	info_file_name=aic-${NPU_TYPE,,}-ops-info

	export PYTHONPATH=${OPS_PROJECT_PATH}:$PYTHONPATH
	export ASCEND_TENSOR_COMPILER_INCLUDE=${ASCEND_HOME_PATH}/atc/include

	python_version=$(python -V)
	arr=($python_version)
	ver=${arr[1]}
	num=(`echo $ver | tr '.' ' '`)
	export HI_PYTHON=python${num[0]}.${num[1]}

	sys_info=$(uname -s)_$(uname -m)
	SYSTEM_INFO=${sys_info,,}

    if [ -z $CANNDEV_DIR ];then
        fn_log "ERROR" $0:$LINENO "env CANNDEV_DIR not exist"
        exit -1
    fi
    if [ -z $OPS_PROJECT_PATH ];then
        fn_log "ERROR" $0:$LINENO "env OPS_PROJECT_PATH not exist"
        exit -1
    fi
	if [ -z $BISHENG_CPP_HOME ];then
        fn_log "ERROR" $0:$LINENO "env BISHENG_CPP_HOME not exist"
        exit -1
    fi

    cd ${CANNDEV_DIR}
    find ./ -name "*.sh" | xargs -x dos2unix
    find ./ -name "*.sh" | xargs -x chmod +x

    cd ${OPS_PROJECT_PATH}
    find ./ -name "*.sh" | xargs -x dos2unix
    find ./ -name "*.sh" | xargs -x chmod +x

	ini_file=$(ls ${OPS_PROJECT_PATH}/op_info_cfg/ai_core/${NPU_TYPE,,}/*)
	OP_TYPE=$(get_name $ini_file)
    canndev_binary_path="${CANNDEV_DIR}/ops/built-in/kernel/binary_script"
	fn_clear_output
    fn_restore_cann ${OP_TYPE} ${NPU_TYPE} ${info_file_name}
    fn_restore_canndev ${OP_TYPE} ${NPU_TYPE} ${info_file_name} ${canndev_binary_path}
    fn_add_impl_to_canndev_and_cann ${OP_TYPE}
    fn_add_info_to_cann ${OP_TYPE} ${NPU_TYPE} ${info_file_name} ${canndev_binary_path}
    fn_generate_binary ${NPU_TYPE} ${canndev_binary_path}
    fn_make_proto_and_tiling
    fn_packing
    fn_restore_cann ${OP_TYPE} ${NPU_TYPE} ${info_file_name}
    fn_restore_canndev ${OP_TYPE} ${NPU_TYPE} ${info_file_name} ${canndev_binary_path}

	chmod 777 -R ${ASCEND_HOME_PATH}/ops/vendors
	chmod 777 -R ${ASCEND_HOME_PATH}/ops/built-in/op_impl/ai_core/tbe/config
}

fn_main "$@"